//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SiteQuery.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System.Collections.Generic;
using System.Data;
using System.Linq;
using Microsoft.SharePoint;

namespace Smart.Common.Entities
{
	/// <summary>
	/// Site data query
	/// </summary>
	public class SiteQuery
	{
		/// <summary>
		/// Executor
		/// </summary>
		public ICamlQueryExecutor Executor { get; protected set; }

		/// <summary>
		/// Query builder
		/// </summary>
		public IQueryBuilder<ICamlQueryExecutor> Query
		{
			get { return Executor.Query; }
		}

		/// <summary>
		/// Query metadata
		/// </summary>
		public MetaFields Meta
		{
			get { return Query.Meta; }
		}

		/// <summary>
		/// Lists filter
		/// </summary>
		public SiteQueryLists Lists { get; protected set; }

		/// <summary>
		/// Scope filter
		/// </summary>
		public SiteQueryScope Scope { get; set; }

		/// <summary>
		/// View fields
		/// </summary>
		public SiteQueryViewFields ViewFields { get; set; }

		/// <summary>
		/// All rows limit
		/// </summary>
		public int RowLimit { get; set; }

		/// <summary>
		/// Set site data query limits
		/// </summary>
		public virtual SiteQuery SetLimits(int maxListsLimit, bool includeHiddenLists, int rowLimit)
		{
			Lists.MaxListsLimit = (maxListsLimit <= 0 ? 0 : maxListsLimit);
			Lists.IncludeHiddenLists = includeHiddenLists;

			RowLimit = (rowLimit <= 0 ? 0 : rowLimit);

			return this;
		}

		/// <summary>
		/// Set <paramref name="serverTemplate"/> lists filter
		/// </summary>
		public virtual SiteQuery SetListsServerTemplate(string serverTemplate)
		{
			Lists.ServerTemplate = serverTemplate;

			return this;
		}

		/// <summary>
		/// Set <paramref name="baseType"/> lists filter
		/// </summary>
		public virtual SiteQuery SetListsBaseType(string baseType)
		{
			Lists.BaseType = baseType;

			return this;
		}

		/// <summary>
		/// Filter all site data with indexed field
		/// </summary>
		public virtual SiteQuery FilterWithIndex(string fieldName, object value)
		{
			Lists.WithIndexField = fieldName;
			Lists.WithIndexFieldValue = value;

			Lists.Lists.Clear();

			return this;
		}

		/// <summary>
		/// Filter by lists collection
		/// </summary>
		public virtual SiteQuery FilterWithLists(bool append, params string[] lists)
		{
			return FilterWithLists(append, (IEnumerable<string>)lists);
		}

		/// <summary>
		/// Filter by lists collection
		/// </summary>
		public virtual SiteQuery FilterWithLists(bool append, IEnumerable<string> lists)
		{
			if (!append)
				Lists.Lists.Clear();

			Lists.Lists.AddRange(lists);

			Lists.WithIndexField = null;
			Lists.WithIndexFieldValue = null;

			return this;
		}

		/// <summary>
		/// Filter by list url
		/// </summary>
		public virtual SiteQuery FilterListsByUrl(bool append, string listUrl)
		{
			if (!append)
				Lists.Lists.Clear();

			AddListByUrl(MetaCtx.GetWeb(Executor.Web), listUrl);

			return this;
		}

		/// <summary>
		/// Add list by url
		/// </summary>
		protected virtual void AddListByUrl(MetaWebScope mw, string listUrl)
		{
			var lid = mw.ListId(listUrl);

			if (!lid.IsEmpty())
			{
				Lists.Lists.Add(lid.ToString("D"));
			}

			foreach (var subWeb in mw.SubWebs)
			{
				AddListByUrl(subWeb.Value, listUrl);
			}
		}

		/// <summary>
		/// Return all fields
		/// </summary>
		public virtual SiteQuery SetAllFields(bool append)
		{
			return SetFields(append, Query.Meta.Fields.Values.Select(field => field.Name));
		}

		/// <summary>
		/// Set fields to return
		/// </summary>
		public virtual SiteQuery SetFields(bool append, params string[] fields)
		{
			return SetFields(append, (IEnumerable<string>)fields);
		}

		/// <summary>
		/// Set fields to return
		/// </summary>
		public virtual SiteQuery SetFields(bool append, IEnumerable<string> fields)
		{
			if (!append)
				ViewFields.Fields.Clear();

			ViewFields.Fields.AddRange(fields.Select(s => new SiteQueryViewFields.ViewFieldInfo { Tag = "FieldRef", Name = s }));

			return this;
		}

		/// <summary>
		/// Return web property
		/// </summary>
		public virtual SiteQuery SetWebProperty(bool append, string name)
		{
			if (!append)
				ViewFields.Fields.Clear();

			ViewFields.Fields.Add(new SiteQueryViewFields.ViewFieldInfo { Tag = "ProjectProperty", Name = name });

			return this;
		}

		/// <summary>
		/// Return list property
		/// </summary>
		public virtual SiteQuery SetListProperty(bool append, string name)
		{
			if (!append)
				ViewFields.Fields.Clear();

			ViewFields.Fields.Add(new SiteQueryViewFields.ViewFieldInfo { Tag = "ListProperty", Name = name });

			return this;
		}

		/// <summary>
		/// Set query scope
		/// </summary>
		public virtual SiteQuery SetScope(SiteQueryScope scope)
		{
			Scope = scope;

			return this;
		}

		/// <summary>
		/// Create SPSiteDataQuery 
		/// </summary>
		public virtual SPSiteDataQuery GetQuery()
		{
			var q = new SPSiteDataQuery
						{
							Lists = Lists.Format(),
							Query = Query.FormatQuery(),
							RowLimit = (uint)RowLimit,
							ViewFields = ViewFields.Format()
						};

			if (Scope != SiteQueryScope.Web)
				q.Webs = string.Format("<Webs Scope=\"{0}\" />", Scope);

			return q;
		}

		public virtual string FormatQuery()
		{
			var query = GetQuery();

			return
				string.Concat(
					query.Webs,
					query.Lists,
					@"<View>",
					@"<Query>",
					query.Query,
					@"</Query>",
					@"<ViewFields>",
					query.ViewFields,
					@"</ViewFields>",
					@"</View>");
		}

		/// <summary>
		/// Return site data
		/// </summary>
		public virtual DataTable GetData()
		{
			return GetData(Executor.Web);
		}

		/// <summary>
		/// Return site data
		/// </summary>
		public virtual DataTable GetData(SPWeb web)
		{
			return web.GetSiteData(GetQuery());
		}

		/// <summary>
		/// Return site data
		/// </summary>
		public virtual EntityList<TEntityType> GetEntities<TEntityType>() where TEntityType : Entity, new()
		{
			return GetEntities<TEntityType>(Executor.Web);
		}

		/// <summary>
		/// Return site data
		/// </summary>
		public virtual EntityList<TEntityType> GetEntities<TEntityType>(SPWeb web) where TEntityType : Entity, new()
		{
			var dt = GetData(web);

			return dt.DefaultView.ToEntities<TEntityType>(Meta);
		}

		/// <summary>
		/// Site data query
		/// </summary>
		public SiteQuery(ICamlQueryExecutor executor)
		{
			Executor = executor;
			Scope = SiteQueryScope.Recursive;

			Lists = new SiteQueryLists(this);
			ViewFields = new SiteQueryViewFields(this);
		}
	}
}